Skip to content

Imaging and metadata tools for security-ir, held by forensic argument rules - #17

Merged
mkhellat merged 3 commits into
mainfrom
feat/ir-imaging-tools
Nov 12, 2026
Merged

mkhellat merged 3 commits into
mainfrom
feat/ir-imaging-tools

Conversation

@mkhellat

Copy link
Copy Markdown
Owner

PR 3b of the phase 10, step 5 plan (decision D14i): imaging and metadata tools for security-ir. Each is held by rules a list of options can't express.

Commits

  • 1a3da08 feat(safety): argument rules for dc3dd, ewfacquire and exiftool (codebud/forensic.py; applied by the safety engine, behind wrappers too).
    • dc3dd: input must be an image file inside the project, never a device. Outputs and logs must be inside the project. hash= and a log are required. wipe=/hwipe=/pat=/tpat= and unknown keys are refused.

    • ewfacquire: -u is required, together with the case metadata -C -e -E -D, -d sha256 and -l. The target and any second target must be inside the project, and the source must be an image file inside the project. Unknown options are refused.

      A deliberate change from the plan, which said to refuse -u: without -u, ewfacquire prompts on stdin, which a step can't answer. Requiring the metadata flags meets the plan's concern (metadata skipped) instead.

    • exiftool: reading only.

      • Refused: =, <, > (write, copy or rename tags) and ${…} (Perl).
      • Also refused: the options that write, run code or read arguments from a file (-o, -overwrite_original, -tagsFromFile, -p, -if…, -config, -api, -@, -w…, -execute, …).
      • Allowed: read options and tag names (exiftool -Artist photo.jpg).
  • c957616 feat(security-ir): image files and metadata, within the forensic rules.
    • Powerful: dc3dd, ewfacquire.
    • Harmless: ewfverify, ewfinfo, exiftool.
    • Acquisition from devices stays with the operator (NIST SP 800-86 §4.2.1).

Testing

1445 tests pass, 60 of them new across test_forensic_rules.py and test_security_ir.py. Ruff is clean, the manual builds without warnings, and make distcheck passes.

Not tested against the binaries: none of the four tools is installed on the dev machine. The rules come from their man pages (dc3dd(1), ewfacquire(1), exiftool(1)) and are tested as rules.

Phase 10, step 5 plan, PR 3b (decision D14i).  Imaging and metadata
tools need rules a list of options cannot express; codebud/forensic.py
holds them, and the safety engine applies them wherever a profile
allow-lists the tool, behind wrappers too (complete mediation).

  - dc3dd: input an image file inside the project, never a device;
    every output and log inside the project; hash= and log=/hlog=
    required (NIST SP 800-86 section 4.2.2, ACPO principle 3); wipe=,
    hwipe=, pat=, tpat= and unknown keys refused.
  - ewfacquire: -u required, and with it the case metadata on the
    command line (-C -e -E -D), -d with sha256, -l, the target (and a
    second one) inside the project, the source an image file inside the
    project; unknown options refused.  The plan said to refuse -u, but
    without it ewfacquire prompts on stdin, which a step cannot answer;
    requiring the metadata flags meets the plan's concern (metadata
    skipped) instead.
  - exiftool: reading only.  Its format strings (${...}), -if and
    -config run Perl, and =, < and > write, copy or rename tags: those,
    and the options that write, run code or read arguments from a file,
    are refused; read options and tag names (-Artist) are allowed.

Paths are judged with symlinks followed.  The rules come from the man
pages; none of the tools is installed on the dev machine, so they are
tested as rules, not against the binaries.  Manual (Safety and Sandbox),
architecture layout, build list and test list updated.
Phase 10, step 5 plan, PR 3b.  security-ir gains the imaging and
metadata tools, now that forensic.py keeps them to evidence handling:

  - powerful (asked every time): dc3dd and ewfacquire, on image files
    inside the project only -- re-imaging and converting (raw to E01),
    hashed and logged;
  - harmless (they only read): ewfverify, ewfinfo, exiftool.

Acquisition from devices stays with the operator, outside codebud
(NIST SP 800-86 section 4.2.1): dc3dd from /dev, dd, and exiftool
writes stay refused; tests pin that.  Manual (Configuration >
Profiles) and NEWS updated.
@mkhellat

Copy link
Copy Markdown
Owner Author

Review summary: changes requested

One full sweep over c957616: conditions → scenarios → each checked against the rules → De Morgan.

Conditions (forensic.py).

  • dc3dd allowed ⇔ ∀ key=value: key ∉ refused ∧ (input ⇒ image file in project) ∧ (output/log ⇒ in project ∧ ¬device) ∧ (other ⇒ known key with valid value) ∧ input present ∧ hash present ∧ log present.
  • ewfacquire allowed ⇔ all options known ∧ path options in project ∧ the required set present ∧ sha256 ∧ one source that is an image in the project.
  • exiftool allowed ⇔ ∀ arg: "${" ∉ arg ∧ (dash-arg ⇒ no =<> ∧ not a refused option).

Correct, verified by tests:

  • Devices refused (symlinks followed).
  • Outside-project paths refused, including a symlink out.
  • Missing hash, log or metadata refused.
  • Wipe and pattern writes refused; unknown keys and options refused.
  • Every exiftool write, Perl and argfile form in the test table refused; reads allowed.
  • The rules hold behind timeout.
  • security-ir lists the tools with the intended powerful/harmless split, and devices and dd stay refused.

De Morgan: allowed, but destroys evidence. Reproduced on c957616; all ALLOWED:

  1. An output may be the input or an existing file. Examples: dc3dd if=evidence/disk.raw of=evidence/disk.raw … (overwrites the source image), of=evidence/report.txt (overwrites another evidence file), log=evidence/disk.raw, ewfacquire … -l evidence/disk.raw …. "Inside the project" isn't "new". Fix: every output, target and log must be a path that doesn't exist yet. That covers the input too, since the input exists. For ewfacquire, the target's segment files (.E01, .Ex01) must not exist either.

Limit, stated in the manual's general section: a file swapped in the instant between the check and the start isn't caught.

Verdict: changes requested. The fix comes as a new commit, then a re-review.

Review of PR #17.  The rules only required outputs to be inside the
project and not a device, so an imaging step could overwrite evidence:
`dc3dd if=evidence/disk.raw of=evidence/disk.raw ...` truncated the
source image, of= or hof= could replace another evidence file, log=
could be the image itself, and ewfacquire's -l, -t or -2 likewise.

Every dc3dd output and log, and ewfacquire's log, target and second
target, must now be a path that does not exist yet -- which also rules
out the input, since it exists.  For an ewfacquire target, a base name,
its segment files (.E01, .Ex01, ...) must not exist either.  Nine new
cases, all allowed before.  Manual updated.
@mkhellat

Copy link
Copy Markdown
Owner Author

Re-review summary: fixed, ready to merge once CI passes

Second sweep over e9e155b.

Finding Fix Verified
Outputs could overwrite the input or other evidence (of= = if=, log= on the image, ewfacquire -l/-t/-2) e9e155b: every output, target and log must be a path that doesn't exist yet (forensic._new_output), which rules out the input too; an ewfacquire target's segment files (.E01, .Ex01, …) must be new 9 new cases, all ALLOWED before, refused now

New in this sweep, correct: existence is checked with lexists, so a dangling symlink planted at an output path also counts as existing. Otherwise a write through it would land wherever it points.

Limit (documented): a file created or swapped in the instant between the check and the start isn't caught.

1454 tests pass, ruff is clean, the manual builds without warnings, and make distcheck passes. Verdict: correct. I'll merge with a merge commit once CI passes on all jobs, macOS included.

@mkhellat

Copy link
Copy Markdown
Owner Author

Merging. The review finding (outputs could overwrite evidence) is fixed and re-verified: every output, target and log must be a new file. CI passes on all five jobs at e9e155b: Ubuntu Py 3.10 and 3.14, macOS Py 3.10 and 3.14, Fedora. Locally, 1454 tests pass and make distcheck passes. Main hasn't moved since the branch was made. A merge commit keeps the three commits as written.

A standing caveat, stated in the PR: the rules come from the tools' man pages and are tested as rules, not against the binaries, since none is installed on the dev machine.

Next, from the step 5 plan: PR 4, target extraction for network tools and the hard scope block (codebud_sec).

@mkhellat
mkhellat merged commit 5b905b7 into main Nov 12, 2026
5 checks passed
@mkhellat
mkhellat deleted the feat/ir-imaging-tools branch November 12, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant